package cn.edu.xmu.orderpayment.payment.Controller;

import cn.edu.xmu.oomall.core.util.Common;
import cn.edu.xmu.oomall.core.util.ReturnNo;
import cn.edu.xmu.oomall.core.util.ReturnObject;
import cn.edu.xmu.orderpayment.payment.Service.PaymentService;
import cn.edu.xmu.orderpayment.payment.constants.Constants;
import cn.edu.xmu.orderpayment.payment.microservice.alipayvo.NotifyBody;
import cn.edu.xmu.orderpayment.payment.microservice.wechatpayvo.WeChatPayPaymentNotifyRetVo;
import cn.edu.xmu.orderpayment.payment.microservice.wechatpayvo.WeChatPayRefundNotifyRetVo;
import cn.edu.xmu.orderpayment.payment.microservice.wechatpayvo.WeChatPayTransactionRetVo;
import cn.edu.xmu.orderpayment.payment.model.vo.*;
import cn.edu.xmu.privilegegateway.annotation.aop.Audit;
import cn.edu.xmu.privilegegateway.annotation.aop.LoginName;
import cn.edu.xmu.privilegegateway.annotation.aop.LoginUser;
import cn.edu.xmu.privilegegateway.annotation.util.InternalReturnObject;
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.time.ZonedDateTime;
import java.time.ZonedDateTime;

import static cn.edu.xmu.oomall.core.util.Common.decorateReturnObject;
import static cn.edu.xmu.oomall.core.util.Common.processFieldErrors;

/**
 * @author Guanlin Wang 22920192204282
 * @date 2021/12/12
 * Controller层传入请求做参数校验
 * 1.基础校验，空字符串，null，字符串长短，数值大小等
 * 2.业务校验，比如传了用户id要检查该用户是否存在，购买的数量是否超库存。是有业务逻辑的。
 * 3.权限校验，比如有没有权限给用户添加订单对于基础校验，可以使用注解来做。Java的validate了解下。
 * 对于业务校验，是在service里做的。对于权限校验，要看权限是怎么设计的。我一般是在controller做的。
 */
@Api(value = "支付模块API", tags = "payment")
@RestController // Restful的Controller对象
@RefreshScope
@RequestMapping(value = "/", produces = "application/json;charset=UTF-8")
public class PaymentController {

    private final Logger logger = LoggerFactory.getLogger(PaymentController.class);

    @Autowired
    private PaymentService paymentService;;// Service层对象

    @Autowired
    private HttpServletResponse httpServletResponse;// HttpServletResponse对象代表服务器的响应. 这个对象中封装了向客户端发送数据、发送响应头, 发送响应状态码的方法

    /**
     * 顾客支付已建立的支付单
     *
     * @param pid 支付id
     * @param paymentPatternBody 付款信息
     * @param bindingResult
     * @param loginUserId
     * @param loginUserName
     * @return
     */
    @ApiOperation(value = "顾客支付已建立的支付单")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType="header", dataType="String", name="authorization", value="用户Token", required = true),
            @ApiImplicitParam(paramType="path", dataType="Integer", name="pid", value = "支付id", required = true),
    })      @ApiImplicitParam(paramType = "body", dataType = "PaymentPatternBody", name = "paymentPatternBody", value = "付款信息", required = true)//VO(View Object): 显示层对象, 通常是 Web 向模板渲染引擎层传输的对象
    @ApiResponses({
            @ApiResponse(code = 0, message = "成功"),
            @ApiResponse(code = 507, message = "订单归属不正确"),
    })
    //@Audit(departName = "payments")
    @PutMapping(value = "/payments/{pid}/pay")
    public Object payOrderPayment(
            @PathVariable("pid") Long pid, //支付id
            @Valid @RequestBody PaymentPatternBody paymentPatternBody, //付款信息
            BindingResult bindingResult, @LoginUser Long loginUserId, @LoginName String loginUserName
    ){
        if(bindingResult.hasErrors()){
            return Common.decorateReturnObject(new ReturnObject(ReturnNo.FIELD_NOTVALID,"传入的RequestBody参数格式不合法"));
        }
        var res = Common.processFieldErrors(bindingResult, httpServletResponse);//res做全局错误处理
        if(res != null) return res;//res不为空说明获取到错误 返回该错误
        var ret = paymentService.payOrderPayment(pid, paymentPatternBody.getPayPattern(), loginUserId, loginUserName);//调用业务逻辑层方法 返回SimplePayment类对象
        if(ret.getCode().equals(ReturnNo.OK)) httpServletResponse.setStatus(HttpStatus.CREATED.value());//设置正常信息
        return Common.decorateReturnObject(ret); //转换装饰器
    }

//    /**
//     * 顾客支付
//     *
//     * @param payBody
//     * @param bindingResult
//     * @param loginUserId
//     * @param loginUserName
//     * @return
//     */
//    @ApiOperation(value = "顾客支付")
//    @ApiImplicitParams({
//            @ApiImplicitParam(paramType = "header", dataType = "String", name = "authorization", value = "用户Token", required = true),
//            @ApiImplicitParam(paramType = "body", dataType = "PayBody", name = "payBody", value = "付款信息", required = true)
//    })
//    @ApiResponses({
//            @ApiResponse(code = 0, message = "成功"),
//            @ApiResponse(code = 902, message = "商品销售时间冲突"),
//            @ApiResponse(code = 947, message = "开始时间不能晚于结束时间"),
//            @ApiResponse(code = 505, message = "限定秒杀或普通"),
//    })
////    @Audit(departName = "payments")
//    @PostMapping(value = "/payments")
//    public Object customerPay(
//            @Validated @RequestBody PayBody payBody,
//            BindingResult bindingResult, @LoginUser Long loginUserId, @LoginName String loginUserName
//    ){
//        if(bindingResult.hasErrors()){
//            return Common.decorateReturnObject(new ReturnObject(ReturnNo.FIELD_NOTVALID,"传入的RequestBody参数格式不合法"));
//        }
//        var ret = Common.processFieldErrors(bindingResult, httpServletResponse);//res做全局错误处理
//        if(ret != null) return ret;//ret不为空说明获取到错误 返回该错误
//        //在此处进行超时判断 => 是否应该移到Service层里?
//        if(payBody.getEndTime().isBefore(payBody.getBeginTime())){
//            return decorateReturnObject(new ReturnObject(ReturnNo.LATE_BEGINTIME));
//        }
//        //去业务逻辑层调用方法
//        ReturnObject res = paymentService.createPayment(payBody, loginUserId, loginUserName);
//        if(res.getCode().equals(ReturnNo.OK)) httpServletResponse.setStatus(HttpStatus.CREATED.value());//设置正常信息
//        return Common.decorateReturnObject(res); //转换装饰器
//    }

    /**
     * 顾客支付
     *
     * @param payBody
     * @param bindingResult
     * @param loginUserId
     * @param loginUserName
     * @return
     */
    @ApiOperation(value = "顾客支付")
    @Audit
    @PostMapping("payments")
    public Object customerPay(
            @LoginUser Long loginUserId, @LoginName String loginUserName,
            @Validated @RequestBody PayBody payBody,
            BindingResult bindingResult
    ){
        Object returnObject = processFieldErrors(bindingResult, httpServletResponse);
        if (null != returnObject) {
            return returnObject;
        }

        if(payBody.getEndTime().isBefore(payBody.getBeginTime())){
            return decorateReturnObject(new ReturnObject(ReturnNo.LATE_BEGINTIME));
        }

        ReturnObject res = paymentService.createPayment(payBody, loginUserId, loginUserName);
        if(res.getCode()!=ReturnNo.OK){
            return decorateReturnObject(res);
        }
        httpServletResponse.setStatus(HttpServletResponse.SC_CREATED);
        return decorateReturnObject(res);

    }


    /**
     * 平台管理员查询支付信息
     *
     * @param shopId
     * @param documentId
     * @param state
     * @param beginTime
     * @param endTime
     * @param page
     * @param pageSize
     * @return
     */
    @ApiOperation(value = "平台管理员查询支付信息")
    @GetMapping("/shops/{shopId}/payment")
    public Object getPaymentInfo(
            @PathVariable Long shopId,
            @RequestParam(required = false) String documentId,
            @RequestParam(required = false) Byte state,
            @RequestParam(value = "beginTime", required = false) @DateTimeFormat(pattern = "uuuu-MM-dd'T'HH:mm:ss.SSSXXX") ZonedDateTime beginTime,
            @RequestParam(value = "endTime", required = false) @DateTimeFormat(pattern = "uuuu-MM-dd'T'HH:mm:ss.SSSXXX") ZonedDateTime endTime,
            @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer pageSize
    ) {
        if (0!=shopId){
            ReturnObject ret = new ReturnObject(ReturnNo.RESOURCE_ID_OUTSCOPE);//不是0，说明不是管理员，返回505错误
            return decorateReturnObject(ret);
        }
        ReturnObject ret = paymentService.getPaymentInfo(documentId,state,beginTime,endTime,page,pageSize);
        return decorateReturnObject(Common.getPageRetVo(ret, SimplePaymentRetVo.class));

    }

    /**
     * 平台管理员查询支付信息详情
     *
     * @param shopId
     * @param id
     * @param loginUserId
     * @param loginUserName
     * @return 返回值为Payment类型
     */
    @ApiOperation(value = "平台管理员查询支付信息详情")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "authorization", value = "用户Token", required = true),
            @ApiImplicitParam(paramType = "path", dataType = "Integer", name = "shopId", value = "管理员id", required = true),
            @ApiImplicitParam(paramType = "path", dataType = "Integer", name = "id", value = "支付id", required = true)
    })
    @ApiResponses({
            @ApiResponse(code = 0, message = "成功"),
            @ApiResponse(code = 505, message = "操作的资源不是自己的对象"),
    })
    @Audit(departName = "shops")
    @GetMapping("/shops/{shopId}/payment/{id}")
    public Object adminGetCertainPaymentInfo(
            @PathVariable(value = "shopId", required = true) Long shopId,// 管理员id
            @PathVariable(name = "id", required = true) Long id, // 支付id -> 注意是路径
            @LoginUser Long loginUserId, @LoginName String loginUserName
    ){
        if(shopId != 0) return Common.decorateReturnObject(new ReturnObject(ReturnNo.RESOURCE_ID_OUTSCOPE, "操作的资源不是自己的对象"));//校验管理员权限无误后
        ReturnObject ret = paymentService.adminGetCertainPaymentInfo(id);// 通过支付id查询退款详情
        return Common.decorateReturnObject(ret);
    }

    /**
     * 平台管理员修改支付信息
     * 只有未对账的状态才能修改，用于手工挂账处理，否则出507错误
     * @param shopId
     * @param id
     * @param modificationBody
     * @param bindingResult
     * @param loginUserId
     * @param loginUserName
     * @return 返回类型为Payment
     */
    @ApiOperation(value = "平台管理员修改退款信息")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "authorization", value = "用户Token", required = true),
            @ApiImplicitParam(paramType = "path", dataType = "Integer", name = "shopId", value = "管理员id", required = true),
            @ApiImplicitParam(paramType = "path", dataType = "Integer", name = "id", value = "支付id", required = true),
            @ApiImplicitParam(paramType = "body", dataType = "ModificationBody", name = "modificationBody", value = "修改信息", required = true)//包含 state 和 descr
    })
    @ApiResponses({
            @ApiResponse(code = 0, message = "成功"),
            @ApiResponse(code = 505, message = "操作的资源不是自己的对象"),
            @ApiResponse(code = 507, message = "当前状态禁止此操作"),
    })
    @Audit(departName = "shops")
    @PutMapping("/shops/{shopId}/payment/{id}")
    public Object adminUpdatePaymentInfo(
            @PathVariable("shopId") Long shopId,
            @PathVariable("id") Long id,
            @Valid @RequestBody ModificationBody modificationBody,
            BindingResult bindingResult, @LoginUser Long loginUserId, @LoginName String loginUserName
    ){
        if(bindingResult.hasErrors()){// 如果bindingResult出错 返回相应错误
            return Common.decorateReturnObject(new ReturnObject(ReturnNo.FIELD_NOTVALID,"传入的RequestBody参数格式不合法"));
        }
        if(shopId != 0){ //校验管理员权限
            return Common.decorateReturnObject(new ReturnObject(ReturnNo.RESOURCE_ID_OUTSCOPE));
        }
        ReturnObject returnObject = paymentService.adminUpdatePaymentInfo(id, modificationBody, loginUserId, loginUserName);
        return Common.decorateReturnObject(returnObject);
    }

    /**
     * 获得支付单的所有状态
     */
    @ApiOperation(value = "获得支付单的所有状态")
    @GetMapping("/payment/states")
    public Object getPaymentStates(){
        ReturnObject ret = paymentService.getPaymentStates();
        return Common.decorateReturnObject(ret);
    }

/// 内部API
    /**
     * 顾客查询自己订单的支付信息
     * 供Order模块调用
     * @param id
     * @return
     */
    @ApiOperation(value = "顾客查询自己订单的支付信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "authorization ", value = "用户token", required = true, dataType = "String", paramType = "header"),
            @ApiImplicitParam(name = "id", value = "订单id", required = true, dataType = "Long", paramType = "path"),
    })
    @ApiResponses({
            @ApiResponse(code = 0, message = "成功"),
    })
    @GetMapping("/orders/{id}/payment")
    public Object customerGetPaymentInfo(
            @PathVariable("id") String id// 订单id
    ){
        if(id == null) return Common.decorateReturnObject(new ReturnObject(ReturnNo.FIELD_NOTVALID,"传入的参数不合法"));
        ReturnObject ret = paymentService.customerGetPaymentInfo(id);
        return Common.decorateReturnObject(ret);
    }

    /**
     * 内部API-指定顾客支付
     * 用于管理员为顾客建立售后支付
     * @param payBody
     * @param bindingResult
     * @param loginUserId
     * @param loginUserName
     * @return
     */
    @ApiOperation(value = "指定顾客支付")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "authorization", value = "用户Token", required = true),
            @ApiImplicitParam(paramType = "body", dataType = "PayBody", name = "payBody", value = "付款信息", required = true)
    })
    @ApiResponses({
            @ApiResponse(code = 0, message = "成功"),
            @ApiResponse(code = 902, message = "商品销售时间冲突"),
            @ApiResponse(code = 947, message = "开始时间不能晚于结束时间"),
            @ApiResponse(code = 505, message = "限定秒杀或普通"),
    })
    @Audit(departName = "payments")
    @PostMapping(value = "/internal/payments")
    public Object adminSpecifyCustomerPay(
            @Valid @RequestBody PayBody payBody,
            BindingResult bindingResult, @LoginUser Long loginUserId, @LoginName String loginUserName
    ){
        if(bindingResult.hasErrors()){
            return Common.decorateReturnObject(new ReturnObject(ReturnNo.FIELD_NOTVALID, "传入的RequestBody参数格式不合法"));
        }
        var res = Common.processFieldErrors(bindingResult, httpServletResponse);//res做全局错误处理
        if(res != null){
            return res;// res不为空说明获取到错误 返回该错误
        }
        var ret = paymentService.createPayment(payBody, loginUserId, loginUserName);//createAfterSalePayment(payBody, loginUserId, loginUserName);
        return Common.decorateReturnObject(ret);
    }


    @ApiOperation(value = "微信支付通知API")
    @PostMapping("/wechat/payment/notify")
    public Object wechatPaymentNotify(
            @RequestBody WeChatPayPaymentNotifyRetVo weChatPayPaymentNotifyRetVo){
        logger.info(ZonedDateTime.now()+":支付系统收到微信传来的回调结果");

        ServletRequestAttributes servletRequestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Constants.servletRequestAttributes=servletRequestAttributes;

        WeChatPayTransactionRetVo weChatPayTransactionRetVo = weChatPayPaymentNotifyRetVo.getResource().getCiphertext();
        ReturnObject res = paymentService.wechatPaymentNotify(weChatPayTransactionRetVo);
        return decorateReturnObject(res);
    }


    @ApiOperation(value = "阿里异步通知API")
    @PostMapping("/alipay/notify")
    public Object alipayNotify(@RequestBody NotifyBody notifyBody){
        logger.info("alipay notify");
//        logger.info(weChatPayPaymentNotifyRetVo);
        ServletRequestAttributes servletRequestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Constants.servletRequestAttributes=servletRequestAttributes;
        ReturnObject res = paymentService.alipayNotify(notifyBody);
        return decorateReturnObject(res);
    }

    /**
     *  内部API：订单获得支付
     */
    @GetMapping("internal/document/{documentId}/payment")
    public InternalReturnObject getPaymentByDocumentId(
            @PathVariable("documentId") String documentId
    ){
        ReturnObject res= paymentService.getPaymentByDocumentId(documentId);

        InternalReturnObject ret = new InternalReturnObject();
        if(res.getData() != null)
            ret.setData(res.getData());
        ret.setErrno(res.getCode().getCode());
        ret.setErrmsg(res.getErrmsg());
        return ret;
    }

    @ApiOperation(value = "内部API支付信息")
    @GetMapping("/internal/shops/{shopId}/payment")
    public InternalReturnObject getPaymentByStateAndTime(
            @PathVariable Long shopId,
            @RequestParam(required = false) String documentId,
            @RequestParam(required = false) Byte state,
            @RequestParam(value = "beginTime", required = false) @DateTimeFormat(pattern = "uuuu-MM-dd'T'HH:mm:ss.SSSXXX") ZonedDateTime beginTime,
            @RequestParam(value = "endTime", required = false) @DateTimeFormat(pattern = "uuuu-MM-dd'T'HH:mm:ss.SSSXXX") ZonedDateTime endTime,
            @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer pageSize
    ) {
        if (0!=shopId){
            return new InternalReturnObject(ReturnNo.RESOURCE_ID_OUTSCOPE.getCode(),ReturnNo.RESOURCE_ID_OUTSCOPE.getMessage());
        }
        ReturnObject res = paymentService.getPaymentInfo(documentId,state,beginTime,endTime,page,pageSize);
        res = Common.getPageRetVo(res, SimplePaymentRetVo.class);

        InternalReturnObject ret = new InternalReturnObject();
        if(res.getData()!=null)
            ret.setData(res.getData());
        ret.setErrno(res.getCode().getCode());
        ret.setErrmsg(res.getErrmsg());
        return ret;
    }


}
